package no.uio.ifi.cop.cl;

import java.util.Set;
import java.util.ArrayList;

import no.uio.ifi.cop.common.Log;

/**
 * A Predicate holds a list of Terms.
 *
 * @version 1.0
 * @author bjarneh@ifi.uio.no
 */

public final class Predicate implements Term, Cloneable{

    private String id;
    private ArrayList<Term> inner;

    public Predicate(String id){
        this.id = id;
        inner = new ArrayList<Term>(10);
    }

    public String getId()  { return id;  }
    public String strType(){ return "Predicate";  }

    public boolean isVariable() { return false; }
    public boolean isPredicate(){ return true;  }
    public boolean isConstant() { return false; }

    public void gatherVariables(Set<String> set){
        for(Term t : inner){
            t.gatherVariables(set);
        }
    }

    // note: this should be called from the open term, i.e.,
    // t1 = p(X,Y) , t2 = p(a,b) => t1.match(subst, t2)
    public boolean match(Substitution subst, Term other){
        if(other.isPredicate() && getId() == other.getId()){
            @SuppressWarnings("unchecked")
            Predicate p_other = (Predicate) other;
            if( length() == p_other.length() ){
                int max = length();
                for( int i = 0; i < max; i++ ){
                    if( ! get(i).match(subst, p_other.get(i) )){
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    // if this match that return subst || null
    public Substitution smatch(Term other){

        Substitution subst = new Substitution();

        if(this.match(subst, other)){
            return subst;
        }

        return null;
    }

    @SuppressWarnings("unchecked")
    public Term apply(Substitution subst){
        if( subst == null ){
            Log.fatal(" Substitution == null | known bug..");
        }

        Term current;
        Variable tmp;
        Term replace;
        int max = length();
        for(int i = 0; i < max; i++){
            current = get(i);
            if( current.isVariable() ){
                tmp = (Variable) current;
                replace = subst.get( tmp );
                if( replace != null ){
                    set(i, replace);
                }
            }else if( current.isPredicate() ){
                current.apply(subst);
            }
        }
        return this;
    }

    public int hashCode() { return toString().hashCode(); }
    public int length()   { return inner.size(); }
    public Term get(int i){ return inner.get(i);  }
    public void set(int i, Term t) { inner.set(i, t); }
    public void addTerm(Term t){ inner.add(t);  }
    
    public Predicate clone(){
        Predicate copy = new Predicate( id );
        for( Term t : inner ){
            copy.addTerm( t.clone() );
        }
        return copy;
    }

    public boolean equals(Object other){

        if( other instanceof Predicate ){

            Predicate p_other = (Predicate) other;

            if( getId() == p_other.getId() && length() == p_other.length() ){
                int max = length();
                for( int i = 0; i < max; i++ ){
                    if(! get(i).equals( p_other.get(i) ) ){
                        return false;
                    }
                }
                return true;
            }
        }
        return false;
    }

    public String toString(){
        // Constants should be removed perhaps now that
        // Predicates are allowed empty term-lists..
        if( inner.size() == 0 ){
            return id;
        }
        StringBuilder sb = new StringBuilder(id);
        sb.append("(");
        int max = inner.size() - 1;
        for(int i = 0; i < max ; i++){
            sb.append(inner.get(i)).append(", ");
        }
        sb.append(inner.get(max)).append(")");
        return sb.toString();
    }

}
